import StringIO
import re
import xml.parsers.expat

class AsxParser:
    def __init__(self):
        pass

    def parse_file(self, file):
        self._init()
        self._parser.Parse(self._prettify(file))
        return self.streams

    def _init(self):
        # The XML _parser can apparently not be reused.
        self._parser = xml.parsers.expat.ParserCreate()
        self._parser.StartElementHandler = self._start_element
        self._parser.EndElementHandler = self._end_element
        self._parser.CharacterDataHandler = self._char_data
        self.entry = {}
        self._stack = []
        self.in_entry = False
        self.streams = []

    def _match_result_lower(self, match_result):
        return match_result.group(0).lower()

    def _prettify(self, file):
        pretty_file = StringIO.StringIO()
        amp_pattern = re.compile('(?i)&(?!amp;)')
        element_pattern = re.compile('<\s*/?\w+')
        for line in file:
            line = amp_pattern.sub('&amp;', line)
            line = element_pattern.sub(self._match_result_lower, line)
            pretty_file.write(line)
        return pretty_file.getvalue()

    def _start_element(self, name, attributes):
        n = name.lower()
        a = {}
        for key, value in attributes.items():
            a[key.lower()] = value
        if n == 'entry':
            self.in_entry = True
        if self.in_entry:
            # TODO warn when overwriting?
            self.entry[n] = a
            self._stack.append(n)

    def _char_data(self, data):
        if self.in_entry:
            n = self._stack[-1]
            if not self.entry[n].has_key(''):
                self.entry[n][''] = ''
            self.entry[n][''] += data

    def _end_element(self, name):
        n = name.lower()
        if self.in_entry:
            self._stack.pop()
        if n == 'entry':
            assert self.in_entry
            stream = {}
            self._populate('ref', 'href', 'file', stream)
            # '' refers to the element's text.
            self._populate('title', '', 'title', stream)
            self.streams.append(stream)
            self.entry = {}
            self.in_entry = False

    def _populate(self, name, attribute, key, stream):
        if not self.entry.has_key(name):
            return
        element = self.entry[name]
        if not element.has_key(attribute):
            return
        stream[key] = element[attribute].replace(r'\r\n\t\v', '')
